package org.confucianismframework.orm.sql.produce;

import org.confucianismframework.orm.TestBean;
import org.confucianismframework.orm.annotation.RegisterSQLStrategy;
import org.confucianismframework.orm.enums.SQLType;

import java.io.File;
import java.lang.annotation.Annotation;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
 * Sql 策略生成工厂
 *
 * @author zy
 */
public class SqlProduceFactory {

    // TODO 加入用户自定义路径
    private static final String SQL_PRODUCE_PACKAGE = "org.confucianismframework.orm.sql.produce.support";

    // 类加载器
    private ClassLoader classLoader;

    // SqlProduce 实现类列表
    private List<Class<? extends SqlProduce>> sqlProduceList;

    /**
     * 单例模式
     */
    private SqlProduceFactory() {
        initialization();
    }

    /**
     * 初始化参数, 并加载 class
     */
    private void initialization() {
        sqlProduceList = new ArrayList<>(10);
        classLoader = getClass().getClassLoader();

        List<File> resources = Arrays.asList(getResources());

        Class<SqlProduce> sqlProduceClazz;

        try {
            sqlProduceClazz = (Class<SqlProduce>) classLoader.loadClass(SqlProduce.class.getName());

            for (File resource : resources) {

                String loadPath = resource.getName().replace(".class", "");

                Class<?> clazz = classLoader.loadClass(SQL_PRODUCE_PACKAGE + "." + loadPath);

                if (!(SqlProduce.class.isAssignableFrom(clazz) && clazz != sqlProduceClazz)) {
                    continue;
                }
                sqlProduceList.add((Class<? extends SqlProduce>) clazz);

            }
        } catch (ClassNotFoundException | RuntimeException e) {
            e.printStackTrace();
        }

    }

    /**
     * SqlProduce 接口的所有实现类, 必须在类上加注解 @RegisterSQLStrategy(SQLType.*)
     * 加入注解后, 工厂则会自动选择 SqlProduce 的实现类
     * 
     * @return sqlProduce 实现类
     */
    public SqlProduce build(SQLType sqlType) {

        try {
            for (Class<? extends SqlProduce> clazz : sqlProduceList) {

                RegisterSQLStrategy registerSQLStrategy = handlerAnnotation(clazz);

                String classAnnotationVal = registerSQLStrategy.value().getValue();

                if (!sqlType.getValue().equalsIgnoreCase(classAnnotationVal)) {
                    continue;
                }
                return clazz.newInstance();

            }
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        throw new RuntimeException("策略获取失败 !");
    }


    private RegisterSQLStrategy handlerAnnotation(Class<? extends SqlProduce> clazz) {
        Annotation[] annotations = clazz.getDeclaredAnnotations();

        if (null == annotations || 0 == annotations.length) {
            return null;
        }

        return (RegisterSQLStrategy) Arrays.stream(annotations)
                .filter(annotation -> annotation instanceof RegisterSQLStrategy)
                .findFirst().get();

    }

    /**
     * 获取扫描的包下面所有的class文件
     */
    private File[] getResources() {
        try {
            File file = new File(classLoader.getResource(SQL_PRODUCE_PACKAGE.replace(".", "/")).toURI());
            return file.listFiles(pathname -> {
                // 只扫描 class 文件
                if (pathname.getName().endsWith(".class")) {
                    return true;
                }
                return false;
            });
        } catch (URISyntaxException e) {
            throw new RuntimeException("未找到策略资源");
        }
    }

    /**
     * 获取实例
     */
    public static SqlProduceFactory instance() {
        return SqlProduceFactoryInstance.instance;
    }

    private static class SqlProduceFactoryInstance {

        private static SqlProduceFactory instance = new SqlProduceFactory();

    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        SqlProduce sqlProduce = SqlProduceFactory.instance().build(SQLType.DELETE);
        TestBean testBean = new TestBean(1, "张三", 18);
        String sql = sqlProduce.produce(testBean);
        System.out.println(sql);
        SqlProduce sqlProduce1 = SqlProduceFactory.instance().build(SQLType.INSERT);
        String sql_1 = sqlProduce1.produce(testBean);
        System.out.println(sql_1);
        SqlProduce sqlProduce2 = SqlProduceFactory.instance().build(SQLType.UPDATE);
        String sql_2 = sqlProduce2.produce(testBean);
        System.out.println(sql_2);
        SqlProduce sqlProduce3 = SqlProduceFactory.instance().build(SQLType.SELECT);
        String sql_3 = sqlProduce3.produce(testBean);
        System.out.println(sql_3);
        float asd = (float) (System.currentTimeMillis() - startTime) / 1000;
        System.out.println(asd);

    }



}
